package packet

import (
	"bytes"
	"encoding/json"
)

type VarPackAdapter struct {
	Info    AdapterInfo
	MinSize int
}

func NewVarPack() *VarPackAdapter {
	out := VarPackAdapter{
		Info: AdapterInfo{
			MaxPackageSize: MaxVarByte4Size,
			Name:           VariableLengthAdapterName,
		},
		MinSize: 1,
	}
	return &out
}

func (adapter *VarPackAdapter) Name() string {
	return VariableLengthAdapterName
}

func (adapter *VarPackAdapter) DumpString() string {
	if adapter == nil {
		return "invalidate adapter"
	}
	outByte, err := json.Marshal(adapter)
	if err == nil {
		var output bytes.Buffer
		if err := json.Indent(&output, outByte, "", "\t"); err != nil {
			return string(outByte)
		}

		return output.String()
	}
	return ""
}

const MaxVarByte1Size = 0b1111111
const MaxVarByte2Size = 0b11111111111111
const MaxVarByte3Size = 0b111111111111111111111
const MaxVarByte4Size = 0b1111111111111111111111111111
const MinVarSize = 1

func (adapter *VarPackAdapter) GenHeaderByLen(packageLen uint32) []byte {
	if packageLen <= MaxVarByte1Size {
		return []byte{
			byte(packageLen),
		}
	}
	if packageLen <= MaxVarByte2Size {
		return []byte{
			byte((packageLen&0b11111110000000)>>7) | 0x80,
			byte(packageLen & 0b1111111),
		}
	}
	if packageLen <= MaxVarByte3Size {
		return []byte{
			byte((packageLen&0b111111100000000000000)>>14) | 0x80,
			byte((packageLen&0b11111110000000)>>7) | 0x80,
			byte(packageLen & 0b1111111),
		}
	}
	if packageLen <= MaxVarByte4Size {
		return []byte{
			byte((packageLen&0b1111111000000000000000000000)>>21) | 0x80,
			byte((packageLen&0b111111100000000000000)>>14) | 0x80,
			byte((packageLen&0b11111110000000)>>7) | 0x80,
			byte(packageLen & 0b1111111),
		}
	}
	return nil
}

func (adapter *VarPackAdapter) GenHeader(orgPack []byte) (header []byte) {
	return adapter.GenHeaderByLen(uint32(len(orgPack)))
}

func (adapter *VarPackAdapter) PackNew(orgPack []byte) (newPackage []byte) {
	orgLen := len(orgPack)
	if orgLen < MinVarSize {
		return nil
	}
	header := adapter.GenHeader(orgPack)
	if header == nil {
		return nil
	}
	headerLen := len(header)
	newPackage = make([]byte, orgLen+headerLen)
	copy(newPackage[0:headerLen], header)
	copy(newPackage[headerLen:], orgPack)
	return
}
func (adapter *VarPackAdapter) PackTo(originPackage []byte, outPackage []byte, withPayload bool) (copiedSize int) {
	orgLen := len(originPackage)
	if orgLen < MinVarSize {
		return 0
	}
	header := adapter.GenHeader(originPackage)
	if header == nil {
		return 0
	}
	headerLen := len(header)
	copy(outPackage[0:headerLen], header)
	if withPayload {
		copy(outPackage[headerLen:], originPackage)
		return headerLen + len(originPackage)
	}
	return headerLen
}

func (adapter *VarPackAdapter) UnPack(inputPackage []byte) (originSize uint32, payloadOffset int32) {
	inputLen := len(inputPackage)
	if inputLen < MinVarSize {
		return 0, -1
	}
	if inputPackage[0] == 0 {
		return 0, -1
	}

	if inputPackage[0] <= MaxVarByte1Size {
		payloadOffset = 1
		originSize = uint32(inputPackage[0])
		return
	}

	if inputLen < 2 {
		return 0, -1
	}

	if inputPackage[1] <= MaxVarByte1Size {
		payloadOffset = 2
		originSize = uint32(inputPackage[1]) | uint32(0x7F&inputPackage[0])<<7
		return
	}
	if inputLen < 3 {
		return 0, -1
	}
	if inputPackage[2] <= MaxVarByte1Size {
		payloadOffset = 3
		originSize = uint32(inputPackage[2]) | uint32(0x7F&inputPackage[1])<<7 | uint32(0x7F&inputPackage[0])<<14
		return
	}
	if inputLen < 4 {
		return 0, -1
	}
	if inputPackage[3] <= 0x7F {
		payloadOffset = 4
		originSize = uint32(inputPackage[3]) | uint32(0x7F&inputPackage[2])<<7 | uint32(0x7F&inputPackage[1])<<14 | uint32(0x7F&inputPackage[0])<<21
		return
	}
	return 0, -1
}
